class Range
class Range is Cool does Iterable does Positional {}
Ranges serve two main purposes: to generate lists of consecutive numbers or strings, and to act as a matcher to check if a number or string is within a certain range.
Ranges are constructed using one of the four possible range operators, which consist of two dots, and optionally a caret which indicates that the endpoint marked with it is excluded from the range.
1 .. 5; # 1 <= $x <= 5
1^.. 5; # 1 < $x <= 5
1 ..^5; # 1 <= $x < 5
1^..^5; # 1 < $x < 5
The caret is also a prefix operator for constructing numeric ranges starting from zero:
my $x = 10;
say ^$x; # same as 0 ..^ $x.Numeric
Iterating a range (or calling the list method) uses the same semantics as
the ++ prefix and postfix operators, i.e., it calls the succ method on
the start point, and then the generated elements.
Ranges always go from small to larger elements; if the start point is bigger than the end point, the range is considered empty.
for 1..5 { .say }; # OUTPUT: Ā«1ā¤2ā¤3ā¤4ā¤5ā¤Ā»
say ('a' ^..^ 'f').list; # OUTPUT: Ā«(b c d e)ā¤Ā»
say 5 ~~ ^5; # OUTPUT: Ā«Falseā¤Ā»
say 4.5 ~~ 0..^5; # OUTPUT: Ā«Trueā¤Ā»
say (1.1..5).list; # OUTPUT: Ā«(1.1 2.1 3.1 4.1)ā¤Ā»
Use the ... sequence operator to produce lists of elements that go from larger to smaller values, or to use offsets other than increment-by-1 and other complex cases.
Use ā or * (Whatever) to indicate an end point to be open-ended.
for 1..* { .say }; # start from 1, continue until stopped
for 1..ā { .say }; # the same
Beware that a WhateverCode end point, instead of a plain Whatever, will go through the range operator and create another WhateverCode which returns a Range:
# A Whatever produces the 1..Inf range
say (1..*).^name; # OUTPUT: Ā«Rangeā¤Ā»
say (1..*); # OUTPUT: Ā«1..Infā¤Ā»
# Upper end point is now a WhateverCode
say (1..*+20).^name; # OUTPUT: Ā«{ ... }ā¤Ā»
say (1..*+20).WHAT; # OUTPUT: Ā«(WhateverCode)ā¤Ā»
say (1..*+20).(22); # OUTPUT: Ā«1..42ā¤Ā»
Ranges implement Positional interface, so its elements can be accessed using an index. In a case when the index given is bigger than the Range object's size, Nil object will be returned. The access works for lazy Range objects as well.
say (1..5)[1]; # OUTPUT: Ā«2ā¤Ā»
say (1..5)[10]; # OUTPUT: Ā«Nilā¤Ā»
say (1..*)[10]; # OUTPUT: Ā«11ā¤Ā»
Ranges in subscripts
A Range can be used in a subscript to get a range of values. Please note that assigning a Range to a scalar container turns the Range into an item. Use binding, @-sigiled containers or a slip to get what you mean.
my @numbers = <4 8 15 16 23 42>;
my $range := 0..2;
.say for @numbers[$range]; # OUTPUT: Ā«4ā¤8ā¤15ā¤Ā»
my @range = 0..2;
.say for @numbers[@range]; # OUTPUT: Ā«4ā¤8ā¤15ā¤Ā»
Shifting and scaling intervals
It is possible to shift or scale the interval of a range:
say (1..10) + 1; # OUTPUT: Ā«2..11ā¤Ā»
say (1..10) - 1; # OUTPUT: Ā«0..9ā¤Ā»
say (1..10) * 2; # OUTPUT: Ā«2..20ā¤Ā»
say (1..10) / 2; # OUTPUT: Ā«0.5..5.0ā¤Ā»
Matching against Ranges
You can use smartmatch to match against Ranges.
say 3 ~~ 1..12; # OUTPUT: Ā«Trueā¤Ā»
say 2..3 ~~ 1..12; # OUTPUT: Ā«Trueā¤Ā»
In Rakudo only, you can use the in-range method for matching
against a range, which in fact is equivalent to smartmatch except it will throw
an exception when out of range, instead of returning False:
say ('×'..'×Ŗ').in-range('×¢'); # OUTPUT: Ā«Trueā¤Ā»
However, if it is not included in the range:
say ('×'..'×Ŗ').in-range('p', "Letter 'p'");
# OUTPUT: Ā«(exit code 1) Letter 'p' out of range. Is: "p", should be in "×".."×Ŗ"ā¤
The second parameter to in-range is the optional message that will be printed
with the exception. It will print Value by default.
Methods
method new
multi method new(Range: \min, \max, :$excludes-min, :$excludes-max)
Creates a new Range with the given minimum and maximum, and with the min and max excluded based on the values passed in the corresponding named arguments.
method ACCEPTS
multi method ACCEPTS(Range:D: Mu \topic)
multi method ACCEPTS(Range:D: Range \topic)
multi method ACCEPTS(Range:D: Cool:D \got)
multi method ACCEPTS(Range:D: Complex:D \got)
Indicates if the Range contains (overlaps with) another Range.
As an example:
my $p = Range.new( 3, 5 );
my $r = Range.new( 1, 10 );
say $p.ACCEPTS( $r ); # OUTPUT: Ā«Falseā¤Ā»
say $r.ACCEPTS( $p ); # OUTPUT: Ā«Trueā¤Ā»
say $r ~~ $p; # OUTPUT: Ā«Falseā¤Ā» (same as $p.ACCEPTS( $r )
say $p ~~ $r; # OUTPUT: Ā«Trueā¤Ā» (same as $r.ACCEPTS( $p )
An infinite Range always contains any other Range, therefore:
say 1..10 ~~ -ā..ā; # OUTPUT: Ā«Trueā¤Ā»
say 1..10 ~~ -ā^..^ā; # OUTPUT: Ā«Trueā¤Ā»
Similarly, a Range with open boundaries often includes other ranges:
say 1..2 ~~ *..10; # OUTPUT: Ā«Trueā¤Ā»
say 2..5 ~~ 1..*; # OUTPUT: Ā«Trueā¤Ā»
It is also possible to use non-numeric ranges, for instance string based ones:
say 'a'..'j' ~~ 'b'..'c'; # OUTPUT: Ā«Falseā¤Ā»
say 'b'..'c' ~~ 'a'..'j'; # OUTPUT: Ā«Trueā¤Ā»
say 'raku' ~~ -ā^..^ā; # OUTPUT: Ā«Trueā¤Ā»
say 'raku' ~~ -ā..ā; # OUTPUT: Ā«Trueā¤Ā»
say 'raku' ~~ 1..*; # OUTPUT: Ā«Trueā¤Ā»
When smartmatching a Range of integers with a Cool (string)
the ACCEPTS methods exploits the before
and after operators in order to check that
the Cool value is overlapping the range:
say 1..10 ~~ '5'; # OUTPUT: Ā«Falseā¤Ā»
say '5' before 1; # OUTPUT: Ā«Falseā¤Ā»
say '5' after 10; # OUTPUT: Ā«Trueā¤Ā»
say '5' ~~ *..10; # OUTPUT: Ā«Falseā¤Ā»
In the above example, since the '5' string is after the 10 integer
value, the Range does not overlap with the specified value.
When matching with a Mu instance (i.e., a generic instance), the cmp operator is used.
method min
method min(Range:D:)
Returns the start point of the range.
say (1..5).min; # OUTPUT: Ā«1ā¤Ā»
say (1^..^5).min; # OUTPUT: Ā«1ā¤Ā»
method excludes-min
method excludes-min(Range:D: --> Bool:D)
Returns True if the start point is excluded from the range, and False
otherwise.
say (1..5).excludes-min; # OUTPUT: Ā«Falseā¤Ā»
say (1^..^5).excludes-min; # OUTPUT: Ā«Trueā¤Ā»
method max
method max(Range:D:)
Returns the end point of the range.
say (1..5).max; # OUTPUT: Ā«5ā¤Ā»
say (1^..^5).max; # OUTPUT: Ā«5ā¤Ā»
method excludes-max
method excludes-max(Range:D: --> Bool:D)
Returns True if the end point is excluded from the range, and False
otherwise.
say (1..5).excludes-max; # OUTPUT: Ā«Falseā¤Ā»
say (1^..^5).excludes-max; # OUTPUT: Ā«Trueā¤Ā»
method bounds
method bounds()
Returns a list consisting of the start and end point.
say (1..5).bounds; # OUTPUT: Ā«(1 5)ā¤Ā»
say (1^..^5).bounds; # OUTPUT: Ā«(1 5)ā¤Ā»
method infinite
method infinite(Range:D: --> Bool:D)
Returns True if either end point was declared with ā or *.
say (1..5).infinite; # OUTPUT: Ā«Falseā¤Ā»
say (1..*).infinite; # OUTPUT: Ā«Trueā¤Ā»
method is-int
method is-int(Range:D: --> Bool:D)
Returns True if both end points are Int values.
say ('a'..'d').is-int; # OUTPUT: Ā«Falseā¤Ā»
say (1..^5).is-int; # OUTPUT: Ā«Trueā¤Ā»
say (1.1..5.5).is-int; # OUTPUT: Ā«Falseā¤Ā»
method int-bounds
proto method int-bounds(|)
multi method int-bounds()
multi method int-bounds($from is rw, $to is rw --> Bool:D)
If the Range is an integer range (as indicated by is-int), then this
method returns a list with the first and last value it will iterate over
(taking into account excludes-min and excludes-max). Returns a
Failure if it is not an integer range.
say (2..5).int-bounds; # OUTPUT: Ā«(2 5)ā¤Ā»
say (2..^5).int-bounds; # OUTPUT: Ā«(2 4)ā¤Ā»
If called with (writable) arguments, these will take the values of the
higher and lower bound and returns whether integer bounds could be determined
from the Range:
if (3..5).int-bounds( my $min, my $max) {
say "$min, $max" ; # OUTPUT: Ā«3, 5ā¤Ā»
}
else {
say "Could not determine integer bounds";
}
method minmax
multi method minmax(Range:D: --> List:D)
If the Range is an integer range (as indicated by is-int), then this
method returns a list with the first and last value it will iterate over (taking
into account excludes-min and excludes-max). If the range is not an
integer range, the method will return a two element list containing the start
and end point of the range unless either of excludes-min or
excludes-max are True in which case a Failure is returned.
my $r1 = (1..5); my $r2 = (1^..5);
say $r1.is-int, ', ', $r2.is-int; # OUTPUT: Ā«True, Trueā¤Ā»
say $r1.excludes-min, ', ', $r2.excludes-min; # OUTPUT: Ā«False, Trueā¤Ā»
say $r1.minmax, ', ', $r2.minmax; # OUTPUT: Ā«(1 5), (2 5)ā¤Ā»
my $r3 = (1.1..5.2); my $r4 = (1.1..^5.2);
say $r3.is-int, ', ', $r4.is-int; # OUTPUT: Ā«False, Falseā¤Ā»
say $r3.excludes-max, ', ', $r4.excludes-max; # OUTPUT: Ā«False, Trueā¤Ā»
say $r3.minmax; # OUTPUT: Ā«(1.1 5.2)ā¤Ā»
say $r4.minmax;
CATCH { default { put .^name, ': ', .Str } };
# OUTPUT: Ā«X::AdHoc: Cannot return minmax on Range with excluded endsā¤Ā»
method elems
method elems(Range:D: --> Numeric:D)
Returns the number of elements in the range, e.g. when being iterated over,
or when used as a List. Returns 0 if the start point is larger than the
end point, including when the start point was specified as ā. Fails when
the Range is lazy, including when the end point was specified as ā or
either end point was specified as *.
say (1..5).elems; # OUTPUT: Ā«5ā¤Ā»
say (1^..^5).elems; # OUTPUT: Ā«3ā¤Ā»
method list
multi method list(Range:D:)
Generates the list of elements that the range represents.
say (1..5).list; # OUTPUT: Ā«(1 2 3 4 5)ā¤Ā»
say (1^..^5).list; # OUTPUT: Ā«(2 3 4)ā¤Ā»
method flat
method flat(Range:D:)
Generates a Seq containing the elements that the range represents.
method pick
multi method pick(Range:D: --> Any:D)
multi method pick(Range:D: $number --> Seq:D)
Performs the same function as Range.list.pick, but attempts to optimize
by not actually generating the list if it is not necessary.
method roll
multi method roll(Range:D: --> Any:D)
multi method roll(Range:D: $number --> Seq:D)
Performs the same function as Range.list.roll, but attempts to optimize
by not actually generating the list if it is not necessary.
method sum
multi method sum(Range:D:)
Returns the sum of all elements in the Range. Throws X::Str::Numeric if an element can not be coerced into Numeric.
(1..10).sum # 55
method reverse
method reverse(Range:D: --> Seq:D)
Returns a Seq where all elements that the Range represents have
been reversed. Note that reversing an infinite Range won't produce any
meaningful results.
say (1^..5).reverse; # OUTPUT: Ā«(5 4 3 2)ā¤Ā»
say ('a'..'d').reverse; # OUTPUT: Ā«(d c b a)ā¤Ā»
say (1..ā).reverse; # OUTPUT: Ā«(Inf Inf Inf ...)ā¤Ā»
method Capture
method Capture(Range:D: --> Capture:D)
Returns a Capture with values of .min .max, .excludes-min, .excludes-max, .infinite, and .is-int as named arguments.
method rand
method rand(Range:D --> Num:D)
Returns a pseudo-random value belonging to the range.
say (1^..5).rand; # OUTPUT: Ā«1.02405550417031ā¤Ā»
say (0.1..0.3).rand; # OUTPUT: Ā«0.2130353370062ā¤Ā»
method EXISTS-POS
multi method EXISTS-POS(Range:D: int \pos)
multi method EXISTS-POS(Range:D: Int \pos)
Returns True if pos is greater than or equal to zero and lower than
self.elems. Returns False otherwise.
say (6..10).EXISTS-POS(2); # OUTPUT: Ā«Trueā¤Ā»
say (6..10).EXISTS-POS(7); # OUTPUT: Ā«Falseā¤Ā»
method AT-POS
multi method AT-POS(Range:D: int \pos)
multi method AT-POS(Range:D: int:D \pos)
Checks if the Int position exists and in that case returns the element in that position.
say (1..4).AT-POS(2) # OUTPUT: Ā«3ā¤Ā»
method raku
multi method raku(Range:D:)
Returns an implementation-specific string that produces an equivalent object when given to EVAL.
say (1..2).raku # OUTPUT: Ā«1..2ā¤Ā»
method fmt
method fmt(|c)
Returns a string where min and max in the Range have been
formatted according to |c.
For more information about parameters, see List.fmt.
say (1..2).fmt("Element: %d", ",") # OUTPUT: Ā«Element: 1,Element: 2ā¤Ā»
method WHICH
multi method WHICH (Range:D:)
This returns a string that identifies the object. The string is composed by the
type of the instance (Range) and the min and max attributes:
say (1..2).WHICH # OUTPUT: Ā«Range|1..2ā¤Ā»
sub infix:<+>
multi infix:<+>(Range:D \r, Real:D \v)
multi infix:<+>(Real:D \v, Range:D \r)
Takes a Real and adds that number to both
boundaries of the Range object. Be careful with
the use of parenthesis.
say (1..2) + 2; # OUTPUT: Ā«3..4ā¤Ā»
say 1..2 + 2; # OUTPUT: Ā«1..4ā¤Ā»
sub infix:<->
multi infix:<->(Range:D \r, Real:D \v)
Takes a Real and subtract that number to both
boundaries of the Range object. Be careful with
the use of parenthesis.
say (1..2) - 1; # OUTPUT: Ā«0..1ā¤Ā»
say 1..2 - 1; # OUTPUT: Ā«1..1ā¤Ā»
sub infix:<*>
multi infix:<*>(Range:D \r, Real:D \v)
multi infix:<*>(Real:D \v, Range:D \r)
Takes a Real and multiply both boundaries
of the Range object by that number.
say (1..2) * 2; # OUTPUT: Ā«2..4ā¤Ā»
sub infix:</>
multi infix:</>(Range:D \r, Real:D \v)
Takes a Real and divide both boundaries
of the Range object by that number.
say (2..4) / 2; # OUTPUT: Ā«1..2ā¤Ā»
sub infix:<cmp>
multi infix:<cmp>(Range:D \a, Range:D \b --> Order:D)
multi infix:<cmp>(Num(Real) \a, Range:D \b --> Order:D)
multi infix:<cmp>(Range:D \a, Num(Real) \b --> Order:D)
multi infix:<cmp>(Positional \a, Range:D \b --> Order:D)
multi infix:<cmp>(Range:D \a, Positional \b --> Order:D)
Compares two Range objects. A Real
operand will be considered as both the starting point and the ending
point of a Range to be compared with the other operand.
A Positional operand will be compared with the
list returned by the .list method
applied to the other operand.
See List infix:<cmp>
say (1..2) cmp (1..2); # OUTPUT: Ā«Sameā¤Ā»
say (1..2) cmp (1..3); # OUTPUT: Ā«Lessā¤Ā»
say (1..4) cmp (1..3); # OUTPUT: Ā«Moreā¤Ā»
say (1..2) cmp 3; # OUTPUT: Ā«Lessā¤Ā»
say (1..2) cmp [1,2]; # OUTPUT: Ā«Sameā¤Ā»